﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;
using System.IO.MemoryMappedFiles;
using System.IO;
using System.Runtime.InteropServices;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Diagnostics;
using XOS_VHD_Tools.VHD;

namespace XOS_VHD_Tools
{
    public sealed class VHDFile : INotifyPropertyChanged, IDisposable
    {


        private readonly MemoryMappedFile _file;

        public VHDFile(string vhdFilePath)
        {
            FilePath = vhdFilePath;

            _file = MemoryMappedFile.CreateFromFile(vhdFilePath, FileMode.Open);

            var mbr = ReadSector(0, 1).ConvertTo<MBR>();
            int i = 0;
            Partitions = mbr.first.Where(t => t.type != PartitionType.ExtendedPartition &&
                                            t.type != PartitionType.ExtendedPartition_LBA &&
                                            t.type != PartitionType.Empty &&
                                            Enum.IsDefined(typeof(PartitionType), t.type)).
                                            Select(t =>
                                                {
                                                    return new VHDPartition(t, this) { Id = i++ };
                                                }).ToArray();
        }

        public event PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propName));
            }
        }

        public void Dispose()
        {
            _file.Dispose();
        }

        public string FilePath { get; private set; }

        public VHDPartition ActivePartition { get { return Partitions.FirstOrDefault(t => t.IsActive); } }

        public VHDPartition[] Partitions { get; private set; }

        public byte[] ReadSector(long sectOffset, int sectToRead)
        {
            // sectOffset * 512;
            var buff = new byte[sectToRead * 512];

            using (var map = _file.CreateViewStream(sectOffset * 512, buff.Length, MemoryMappedFileAccess.Read))
            {
                map.Read(buff, 0, buff.Length);
                Trace.WriteLine("ReadSectors(): LBA=0x" + sectOffset.ToString("X8") + "; sect count: " + sectToRead);
                return buff;
            }
        }

        public void WriteSector(byte[] bytes, long sectOffset, int sectToWrite)
        {
            if (bytes == null)
            {
                throw new ArgumentNullException("bytes");
            }

            if (bytes.Length != sectToWrite * 512)
            {
                throw new ArgumentOutOfRangeException();
            }

            using (var map = _file.CreateViewStream(sectOffset * 512, bytes.Length, MemoryMappedFileAccess.Write))
            {
                Trace.WriteLine("WriteSector(): LBA=0x" + sectOffset.ToString("X8") + "; sect count: " + sectToWrite);
                map.Write(bytes, 0, bytes.Length);
                map.Flush();
            }
        }
    }
}
